package com.awesumgames.awesum;

import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
//import android.opengl.EGLConfig;
import android.opengl.Matrix;
import android.util.Log;

import com.awesumgames.awesum.AwesumGlobal.Awesum;
import com.awesumgames.awesum.AwesumGlobal.Awesum.Load;
import com.awesumgames.awesum.controls.Control.Ouya;
import com.awesumgames.awesum.controls.Control.Touch;
import com.awesumgames.awesum.physics.Physics;
import com.awesumgames.awesum.Camera;
import com.awesumgames.awesum.Shader.ArgType;

public final class Game {
	public interface GameListener {
	    public void ready();
	}
	private static List<GameListener> listeners = new ArrayList<GameListener>();
	public static void addListener(GameListener gl) { listeners.add(gl); }
	private static boolean ready = false;
	
	public static Activity activity = null;
	
	public static List<Camera> cameras = null;
	public static SceneCollection scenes = new SceneCollection();
	public static Camera camera = new Camera();
	public static Scene scene;
	public static Physics phys = null;
	public static Vector3 accelerometer = new Vector3();

    public static Boolean pauseInput = Boolean.FALSE;//.valueOf(false);
    public static Boolean lock = Boolean.FALSE;

    public static Boolean stereo = false;
    static int eye = 0; //left = 0, right = 1, package level only
    public static float eyeDist = .1f; //distance to the left and right of the camera to render each eye
    public static int screenWidth = 0;
    public static int screenHeight = 0;
    public static int renderWidth = 0;
    public static int renderHeight = 0;
	
	public static float[] modelMatrix = new float[16];
	public static float[] viewMatrix = new float[16];
	public static float[] projectionMatrix = new float[16];
	public static float[] mvpMatrix = new float[16];
	public static float[] mvMatrix = new float[16];
	//TODO: the following is a hack, figure out a better way to do relative drawings
	public static boolean relative = false;

	public static final int FloatSize = 4;
	public static final int ShortSize = 2;
	
	public static float timeStep = 1f / 60f;
	
	//TODO: addCamera function, update camera reference
	
	public static void setup() {
		cameras = new ArrayList<Camera>();
		cameras.add(camera);
		
		GLES20.glEnable(GLES20.GL_DEPTH_TEST);
		GLES20.glClearColor(0f, 0f, 0f, 1f); 

		/*try {
			Shader color = Shaders.create("color");
			Awesum.debug("compiling cv");
			color.compile(R.raw.color_vertex, GLES20.GL_VERTEX_SHADER);
			Awesum.debug("compiling cf");
			color.compile(R.raw.color_fragment, GLES20.GL_FRAGMENT_SHADER);
			color.link();
			color.addU("u_mvp", ArgType.MVP);
			color.addU("u_mv", ArgType.MV);
			color.addU("u_lightPos", ArgType.Light);
			color.addA("a_pos", ArgType.Pos);
			color.addA("a_norm", ArgType.Normal);
			color.addA("a_color", ArgType.Color);
		} catch (Exception e) {
			Log.d("error", "Error loading color shader: " + e.getMessage());
		}*/
		try {
			Shader color = Shaders.create("color");
			Awesum.debug("compiling csv");
			color.compile(R.raw.color_sep_vertex, GLES20.GL_VERTEX_SHADER);
			Awesum.debug("compiling csf");
			color.compile(R.raw.color_sep_fragment, GLES20.GL_FRAGMENT_SHADER);
			color.link();
			color.addU("u_mvp", ArgType.MVP);
			color.addU("u_mv", ArgType.MV);
			color.addU("u_lightPos", ArgType.Light);
			color.addU("u_color", ArgType.Color);
			color.addA("a_pos", ArgType.Pos);
			color.addA("a_norm", ArgType.Normal);
		} catch (Exception e) {
			Log.d("error", "Error loading color sep shader: " + e.getMessage());
		}
		try {	
			Shader tex = Shaders.create("tex");
			Awesum.debug("compiling tv");
			tex.compile(R.raw.texture_vertex, GLES20.GL_VERTEX_SHADER);
			Awesum.debug("compiling tf");
			tex.compile(R.raw.texture_fragment, GLES20.GL_FRAGMENT_SHADER);
			tex.link();
			tex.addU("u_mvp", ArgType.MVP);
			tex.addA("a_texC", ArgType.TexCoord);
			
			//texture = GLES20.glGetUniformLocation(program, "u_tex");
	        //texCoord = GLES20.glGetAttribLocation(program, "a_texC");
			//GLES20.glEnableVertexAttribArray(Game.Shaders.texCoord);
			
		} catch (Exception e) {
			Log.d("error", "Error loading texture shader: " + e.getMessage());
		}
		
		//Shaders.setup();
		
		//GLES20.glEnableVertexAttribArray(GLES20.GL_VERTEX_ATTRIB_ARRAY_ENABLED);
	}
	
	public static void startPhysics() {
		phys = new Physics();
		phys.load();
	}
	
	public static void updateScreen(int width, int height) {
		screenWidth = width;
        renderHeight = screenHeight = height;
        renderWidth = stereo ? width / 2 : width;
        GLES20.glViewport(0, 0, renderWidth, renderHeight);
        
        if (!ready) {
	        for (GameListener gl : listeners)
	            gl.ready();
	        ready = true;
        }
	}

	public static void draw() {
		//Don't call super.draw, this should only run the topmost concurrent scenes
		if (scene != null) {
			
			GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
			
			if (stereo) {
				int halfWidth = screenWidth / 2;
				GLES20.glViewport(0, 0, halfWidth, screenHeight);
				eye = 0;
				scene.draw();
				
				GLES20.glViewport(halfWidth, 0, halfWidth, screenHeight);
				eye = 1;
			}
			scene.draw();
		}
	}

	public static void step(float seconds) {
		//Don't call super.step, this should only run the topmost concurrent scenes
		//OuyaController.startOfFrame();
		if (phys != null)
			phys.step(seconds);
		if (scene != null) {
			Ouya.update();
			scene.step();
			Touch.reset();
			//TODO: check to run scenes below
		}
	}
	
	public static float[] getMVP() {
		Matrix.multiplyMM(mvMatrix, 0, viewMatrix, 0, modelMatrix, 0);
		Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvMatrix, 0);
		return mvpMatrix;
	}
	public static void setProjection2d() {
		//float[] ortho = ortho2d(0f, 0f, Game.renderWidth, Game.renderHeight);
		float ratio = (float)Game.renderWidth / (float)Game.renderHeight;
		//Matrix.orthoM(Game.projectionMatrix, 0, -ratio, ratio, -1, 1, -10, 10);
		Matrix.orthoM(Game.projectionMatrix, 0, 0, ratio, 0, 1, -10, 10);
		//GLES20.glUniformMatrix4fv(Shaders.mvp, 4, false, ortho, 0);
	}
	public static void setProjection3d() {
		final float ratio = (float) Game.renderWidth / Game.renderHeight;
		Matrix.perspectiveM(Game.projectionMatrix, 0, 45f, ratio, .1f, 500f);
		
		
		//float[] ortho = ortho2d(0f, 0f, Game.screenWidth, Game.screenHeight);
		//Matrix.orthoM(Game.projectionMatrix, 0, -ratio, ratio, -1, 1, -10, 10);


		//Matrix.orthoM(Game.projectionMatrix, 0, -ratio, ratio, -3f, 1f, -10f, 20f);

		/*int c = 4;
		float hw = ratio * c, hh = c;
		Matrix.orthoM(Game.projectionMatrix, 0, -hw, hw, -hh, hh, -10f, 20f);
		*/
	}
	
	public static final class Shaders {
		public static Map<String, Shader> list = new HashMap<String, Shader>();
		//public static Map<Integer, Integer> args = new HashMap<Integer, Integer>();
		public static ArrayList<Integer> programs = new ArrayList<Integer>();
		public static Shader current = null;
		
		public static final Shader create(String name) {
			Shader s = new Shader();
			list.put(name, s);
			return s;
		}
		
		public static final Shader	item(String name)	{ return list.get(name); }
		public static final void	use	(String program){ list.get(program).use(); }
	}
	
	public static final class Textures {
		public static Map<Integer, Integer> list = new HashMap<Integer, Integer>();
		
		public static final int load(int resourceId) {
			if (phys != null)
				phys.load();
			if (list.containsKey(resourceId))
				return list.get(resourceId);
			
			final int[] handle = new int[1];
			GLES20.glGenTextures(1, handle, 0);
			if (handle[0] != 0) {
				final BitmapFactory.Options options = new BitmapFactory.Options();
				options.inScaled = false;
				
				final Bitmap bitmap = BitmapFactory.decodeResource(Awesum.context.getResources(), resourceId, options);
				
				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, handle[0]);
				
				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
				
				GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
				
				bitmap.recycle();
			}
			
			if (handle[0] <= 0)
				Awesum.debug("Error loading texture: " + GLES20.glGetError());
			else
				list.put(resourceId, handle[0]);
			
			return handle[0];
		}
	}
}
